﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Collections.ObjectModel;
using System.Reflection;
using System.ServiceModel;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

using MrCRM.Silverlight;
using MrCRM.Silverlight.SynchronousWCF;

using SilverCrmSoap.CrmSdk;

namespace Pogo.Xrm.Silverlight.UserObjectMigration
{
	public partial class MainPage : UserControl
	{
		#region Local Classes
		public class EntityData
		{
			public int EntityTypeCode { get; set; }
			public string Text { get; set; }
		}

		public class ObjectData
		{
			public Guid Id { get; set; }
			public string Text { get; set; }
		}
		#endregion

		#region Private Members
		OrganizationServiceClientFactory _orgClientFactory = null;
		#endregion

		#region Initialisation
		public MainPage()
		{
			InitializeComponent();

			// initialise organization service client factory
			this._orgClientFactory = new OrganizationServiceClientFactory(ServerUtility.GetServerUrl());
		}
		#endregion

		#region Control Events
		private void radTypeView_Click(object sender, RoutedEventArgs e)
		{
			this.InitialiseControls();
			this.LoadEntities();
		}

		private void radTypeChart_Click(object sender, RoutedEventArgs e)
		{
			this.InitialiseControls();
			this.LoadEntities();
		}

		private void radTypeDashboard_Click(object sender, RoutedEventArgs e)
		{
			this.InitialiseControls();
			this.LoadDashboards();
		}

		private void cboEntity_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			if (this.cboEntity.SelectedIndex < 0) { return; }

			int entityTypeCode = ((EntityData)this.cboEntity.SelectedItem).EntityTypeCode;

			if (this.radTypeChart.IsChecked.GetValueOrDefault())
			{
				this.LoadCharts(entityTypeCode);
			}
			else if (this.radTypeView.IsChecked.GetValueOrDefault())
			{
				this.LoadViews(entityTypeCode);
			}
		}

		private void lstObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			if (this.lstObject.SelectedIndex >= 0)
			{
				this.btnConvert.IsEnabled = true;
			}
		}

		private void btnConvert_Click(object sender, RoutedEventArgs e)
		{
			if (this.radTypeView.IsChecked.GetValueOrDefault())
			{
				Guid viewId = (this.lstObject.SelectedItem as ObjectData).Id;

				this.ConvertView(viewId);
			}
			else if (this.radTypeChart.IsChecked.GetValueOrDefault())
			{
				Guid chartId = (this.lstObject.SelectedItem as ObjectData).Id;

				this.ConvertChart(chartId);
			}
			else if (this.radTypeDashboard.IsChecked.GetValueOrDefault())
			{
				Guid dashboardId = (this.lstObject.SelectedItem as ObjectData).Id;

				this.ConvertDashboard(dashboardId);
			}
		}

		private void InitialiseControls()
		{
			this.cboEntity.IsEnabled = false;
			this.cboEntity.ItemsSource = null;

			this.lstObject.IsEnabled = false;
			this.lstObject.ItemsSource = null;

			this.btnConvert.IsEnabled = false;
		}
		#endregion

		#region Data Update
		private void ConvertDashboard(Guid dashboardId)
		{
			ThreadPool.QueueUserWorkItem(x =>
			{
				// retrieve user form
				ColumnSet columns = new ColumnSet
				{
					AllColumns = true,
					Columns = new ObservableCollection<string>()
				};

				RetrieveCompletedEventArgs retrieveArgs = this._orgClientFactory.Create().SynchronousCall<RetrieveCompletedEventArgs>("Retrieve", "userform", dashboardId, columns);

				Entity userForm = retrieveArgs.Result;

				// check if dashboard contains User elements
				XDocument xmlDoc = XDocument.Parse((string)userForm["formxml"]);
				var userObjects = xmlDoc.XPathSelectElements(@"//control[parameters/IsUserView/text() = 'true' or parameters/IsUserChart/text() = 'true']");

				if (userObjects.Count() > 0)
				{
					Dispatcher.BeginInvoke(() =>
					{
						MessageBox.Show("Dashboard contains User elements, which cannot currently be migrated");
					});

					return;
				}

				// create system form
				Entity systemForm = new Entity();
				systemForm.LogicalName = "systemform";
				systemForm.Attributes = new AttributeCollection();
				systemForm.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "name", Value = GetAttribute(userForm, "name") });
				systemForm.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "canbedeleted", Value = new BooleanManagedProperty() { Value = true } });
				systemForm.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "description", Value = GetAttribute(userForm, "description") });
				systemForm.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "formxml", Value = GetAttribute(userForm, "formxml") });
				systemForm.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "iscustomizable", Value = new BooleanManagedProperty() { Value = true } });
				systemForm.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "isdefault", Value = false });
				systemForm.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "objecttypecode", Value = GetAttribute(userForm, "objecttypecode") });
				systemForm.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "type", Value = GetAttribute(userForm, "type") });

				CreateCompletedEventArgs createArgs = this._orgClientFactory.Create().SynchronousCall<CreateCompletedEventArgs>("Create", systemForm);

				Guid newDashboardId = createArgs.Result;

				Dispatcher.BeginInvoke(() =>
				{
					if (newDashboardId != Guid.Empty)
					{
						MessageBox.Show("Dashboard Converted!");
					}
					else
					{
						MessageBox.Show("Dashboard Conversion failed!");
					}
				});
			});
		}

		private void ConvertChart(Guid chartId)
		{
			ThreadPool.QueueUserWorkItem(x =>
			{
				// retrieve user chart
				ColumnSet columns = new ColumnSet
				{
					AllColumns = true,
					Columns = new ObservableCollection<string>()
				};

				RetrieveCompletedEventArgs retrieveArgs = this._orgClientFactory.Create().SynchronousCall<RetrieveCompletedEventArgs>("Retrieve", "userqueryvisualization", chartId, columns);

				Entity userQueryVisualisation = retrieveArgs.Result;

				// create system chart
				Entity savedQueryVisualisation = new Entity();
				savedQueryVisualisation.LogicalName = "savedqueryvisualization";
				savedQueryVisualisation.Attributes = new AttributeCollection();
				savedQueryVisualisation.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "name", Value = GetAttribute(userQueryVisualisation, "name") });
				savedQueryVisualisation.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "datadescription", Value = GetAttribute(userQueryVisualisation, "datadescription") });
				savedQueryVisualisation.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "description", Value = GetAttribute(userQueryVisualisation, "description") });
				savedQueryVisualisation.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "iscustomizable", Value = new BooleanManagedProperty() { Value = true } });
				savedQueryVisualisation.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "isdefault", Value = false });
				savedQueryVisualisation.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "presentationdescription", Value = GetAttribute(userQueryVisualisation, "presentationdescription") });
				savedQueryVisualisation.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "primaryentitytypecode", Value = GetAttribute(userQueryVisualisation, "primaryentitytypecode") });
				savedQueryVisualisation.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "webresourceid", Value = GetAttribute(userQueryVisualisation, "webresourceid") });

				CreateCompletedEventArgs createArgs = this._orgClientFactory.Create().SynchronousCall<CreateCompletedEventArgs>("Create", savedQueryVisualisation);

				Guid newQueryVisualisationId = createArgs.Result;

				Dispatcher.BeginInvoke(() =>
				{
					if (newQueryVisualisationId != Guid.Empty)
					{
						MessageBox.Show("Chart Converted!");
					}
					else
					{
						MessageBox.Show("Chart Conversion failed!");
					}
				});
			});
		}

		private void ConvertView(Guid viewId)
		{
			ThreadPool.QueueUserWorkItem(x =>
			{
				// retrieve user query
				ColumnSet columns = new ColumnSet
				{
					AllColumns = true,
					Columns = new ObservableCollection<string>()
				};

				RetrieveCompletedEventArgs retrieveArgs = this._orgClientFactory.Create().SynchronousCall<RetrieveCompletedEventArgs>("Retrieve", "userquery", viewId, columns);

				Entity userQuery = retrieveArgs.Result;

				// create system query
				Entity savedQuery = new Entity();
				savedQuery.LogicalName = "savedquery";
				savedQuery.Attributes = new AttributeCollection();
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "name", Value = GetAttribute(userQuery, "name") });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "advancedgroupby", Value = GetAttribute(userQuery, "advancedgroupby") });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "canbedeleted", Value = new BooleanManagedProperty() { Value = true } });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "columnsetxml", Value = GetAttribute(userQuery, "columnsetxml") });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "conditionalformatting", Value = GetAttribute(userQuery, "conditionalformatting") });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "description", Value = GetAttribute(userQuery, "description") });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "fetchxml", Value = GetAttribute(userQuery, "fetchxml") });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "iscustomizable", Value = new BooleanManagedProperty() { Value = true } });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "isdefault", Value = false });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "layoutxml", Value = GetAttribute(userQuery, "layoutxml") });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "querytype", Value = GetAttribute(userQuery, "querytype") });
				savedQuery.Attributes.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() { Key = "returnedtypecode", Value = GetAttribute(userQuery, "returnedtypecode") });

				CreateCompletedEventArgs createArgs = this._orgClientFactory.Create().SynchronousCall<CreateCompletedEventArgs>("Create", savedQuery);

				Guid newQueryId = createArgs.Result;

				Dispatcher.BeginInvoke(() =>
				{
					if (newQueryId != Guid.Empty)
					{
						MessageBox.Show("View Converted!");
					}
					else
					{
						MessageBox.Show("View Conversion failed!");
					}
				});
			});
		}

		private object GetAttribute(Entity entity, string attributeName)
		{
			if (entity.Attributes.ContainsKey<string, object>(attributeName))
			{
				return entity[attributeName];
			}
			else
			{
				return null;
			}
		}
		#endregion

		#region Data Retrieval
		private void LoadEntities()
		{
			ThreadPool.QueueUserWorkItem(x =>
			{
				OrganizationRequest req = new OrganizationRequest() { RequestName = "RetrieveAllEntities" };
				req["EntityFilters"] = EntityFilters.Entity;
				req["RetrieveAsIfPublished"] = false;

				ExecuteCompletedEventArgs execArgs = this._orgClientFactory.Create().SynchronousCall<ExecuteCompletedEventArgs>("Execute", req);

				OrganizationResponse response = execArgs.Result;
				ObservableCollection<EntityMetadata> entities = (ObservableCollection<EntityMetadata>)response["EntityMetadata"];

				Dispatcher.BeginInvoke(() =>
				{
					List<EntityData> itemsEntity = new List<EntityData>();

					foreach (EntityMetadata entity in entities)
					{
						if (entity.CanTriggerWorkflow.GetValueOrDefault())
						{
							itemsEntity.Add(new EntityData
							{
								EntityTypeCode = entity.ObjectTypeCode.Value,
								Text = string.Format("{0} ({1})", entity.DisplayName.LocalizedLabels[0].Label, entity.LogicalName)
							});
						}
					}

					this.cboEntity.ItemsSource = itemsEntity.OrderBy(item => item.Text);
					this.cboEntity.IsEnabled = true;
				});
			});
		}

		private void LoadDashboards()
		{
			ThreadPool.QueueUserWorkItem(x =>
			{
				ConditionExpression condition1 = new ConditionExpression()
				{
					AttributeName = "statecode",
					Operator = ConditionOperator.Equal,
					Values = { 0 }
				};

				FilterExpression filter = new FilterExpression();
				filter.Conditions.Add(condition1);

				QueryExpression query = new QueryExpression()
				{
					EntityName = "userform",
					ColumnSet = new ColumnSet
					{
						Columns = new ObservableCollection<string>
						{
							"userformid",
							"name"
						}
					},
					Criteria = filter
				};

				RetrieveMultipleCompletedEventArgs retrieveMultipleArgs = this._orgClientFactory.Create().SynchronousCall<RetrieveMultipleCompletedEventArgs>("RetrieveMultiple", query);

				EntityCollection userForms = retrieveMultipleArgs.Result;

				Dispatcher.BeginInvoke(() =>
				{
					List<ObjectData> items = new List<ObjectData>();

					foreach (Entity userForm in userForms.Entities)
					{
						items.Add(new ObjectData
						{
							Id = userForm.Id,
							Text = (string)userForm["name"]
						});
					}

					this.lstObject.ItemsSource = items.OrderBy(item => item.Text);
					this.lstObject.IsEnabled = true;
				});
			});
		}

		private void LoadCharts(int entityTypeCode)
		{
			ThreadPool.QueueUserWorkItem(x =>
			{
				ConditionExpression condition1 = new ConditionExpression()
				{
					AttributeName = "primaryentitytypecode",
					Operator = ConditionOperator.Equal,
					Values = { entityTypeCode }
				};
				ConditionExpression condition2 = new ConditionExpression()
				{
					AttributeName = "statecode",
					Operator = ConditionOperator.Equal,
					Values = { 0 }
				};

				FilterExpression filter = new FilterExpression();
				filter.Conditions.Add(condition1);
				filter.Conditions.Add(condition2);

				QueryExpression query = new QueryExpression()
				{
					EntityName = "userqueryvisualization",
					ColumnSet = new ColumnSet
					{
						Columns = new ObservableCollection<string>
						{
							"userqueryvisualizationid",
							"name"
						}
					},
					Criteria = filter
				};

				RetrieveMultipleCompletedEventArgs retrieveMultipleArgs = this._orgClientFactory.Create().SynchronousCall<RetrieveMultipleCompletedEventArgs>("RetrieveMultiple", query);

				EntityCollection userQueryVisualizations = retrieveMultipleArgs.Result;

				Dispatcher.BeginInvoke(() =>
				{
					List<ObjectData> items = new List<ObjectData>();

					foreach (Entity userQueryVisualization in userQueryVisualizations.Entities)
					{
						items.Add(new ObjectData
						{
							Id = userQueryVisualization.Id,
							Text = (string)userQueryVisualization["name"]
						});
					}

					this.lstObject.ItemsSource = items.OrderBy(item => item.Text);
					this.lstObject.IsEnabled = true;
				});
			});
		}

		private void LoadViews(int entityTypeCode)
		{
			ThreadPool.QueueUserWorkItem(x =>
			{
				ConditionExpression condition1 = new ConditionExpression()
				{
					AttributeName = "returnedtypecode",
					Operator = ConditionOperator.Equal,
					Values = { entityTypeCode }
				};
				ConditionExpression condition2 = new ConditionExpression()
				{
					AttributeName = "statecode",
					Operator = ConditionOperator.Equal,
					Values = { 0 }
				};

				FilterExpression filter = new FilterExpression();
				filter.Conditions.Add(condition1);
				filter.Conditions.Add(condition2);

				QueryExpression query = new QueryExpression()
				{
					EntityName = "userquery",
					ColumnSet = new ColumnSet
					{
						Columns = new ObservableCollection<string>
						{
							"userqueryid",
							"name"
						}
					},
					Criteria = filter
				};

				RetrieveMultipleCompletedEventArgs retrieveMultipleArgs = this._orgClientFactory.Create().SynchronousCall<RetrieveMultipleCompletedEventArgs>("RetrieveMultiple", query);

				EntityCollection userQueries = retrieveMultipleArgs.Result;

				Dispatcher.BeginInvoke(() =>
				{
					List<ObjectData> items = new List<ObjectData>();

					foreach (Entity userQuery in userQueries.Entities)
					{
						items.Add(new ObjectData
						{
							Id = userQuery.Id,
							Text = (string)userQuery["name"]
						});
					}

					this.lstObject.ItemsSource = items.OrderBy(item => item.Text);
					this.lstObject.IsEnabled = true;
				});
			});
		}
		#endregion
	}
}
